package com.gs.netty.core.handler.method;

import com.gs.netty.core.resolver.HandlerMethodReturnValueHandler;
import com.gs.netty.core.resolver.args.HandlerMethodArgumentResolver;
import org.springframework.beans.factory.BeanFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 抽象的方法处理器
 * @param <T>
 */
public abstract class AbstractHandlerMethod<T> extends HandlerMethod {

    public static final Object[] EMPTY_ARGS = new Object[0];
    /**
     * 参数解析器
     */
    protected HandlerMethodArgumentResolver argumentResolvers;
    /**
     * 结果处理器
     */
    protected HandlerMethodReturnValueHandler returnValueHandlers;


    public AbstractHandlerMethod(String beanName, Method method, BeanFactory beanFactory) {
        super(beanName, method, beanFactory);
    }

    public AbstractHandlerMethod(HandlerMethod handlerMethod, Object handler) {
        super(handlerMethod, handler);
    }

    public AbstractHandlerMethod(Object bean, Method method) {
        super(bean, method);
    }


    /**
     * 执行方法处理器
     * @param request
     * @param providedArgs
     * @return
     * @throws Throwable
     */
    protected abstract void invokeAndHandle(T request,Object... providedArgs) throws Throwable;


    public HandlerMethodArgumentResolver getArgumentResolvers() {
        return argumentResolvers;
    }

    public void setArgumentResolvers(HandlerMethodArgumentResolver argumentResolvers) {
        this.argumentResolvers = argumentResolvers;
    }

    public HandlerMethodReturnValueHandler getReturnValueHandlers() {
        return returnValueHandlers;
    }

    public void setReturnValueHandlers(HandlerMethodReturnValueHandler returnValueHandlers) {
        this.returnValueHandlers = returnValueHandlers;
    }

    /**
     * 执行方法
     * @param args
     * @return
     * @throws Exception
     */
    protected Object doInvoke(Object... args) throws Exception {

        Method method = getBridgedMethod();
        try {
            // 方法调用
            return method.invoke(getBean(), args);
        }  catch (IllegalArgumentException ex) {
            assertTargetBean(method, getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        } catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            // 方法调用异常 ，找到具体异常
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }

    protected void assertTargetBean(Method method, Object targetBean, Object[] args) {
        Class<?> methodDeclaringClass = method.getDeclaringClass();
        Class<?> targetBeanClass = targetBean.getClass();
        if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
            String text = "The mapped handler method class '" + methodDeclaringClass.getName() +
                    "' is not an instance of the actual controller bean class '" +
                    targetBeanClass.getName() + "'. If the controller requires proxying " +
                    "(e.g. due to @Transactional), please use class-based proxying.";
            throw new IllegalStateException(formatInvokeError(text, args));
        }
    }

    protected String formatInvokeError(String text, Object[] args) {
        String formattedArgs = IntStream.range(0, args.length)
                .mapToObj(i -> (args[i] != null ?
                        "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" :
                        "[" + i + "] [null]"))
                .collect(Collectors.joining(",\n", " ", " "));
        return text + "\n" +
                "Controller [" + getBeanType().getName() + "]\n" +
                "Method [" + getBridgedMethod().toGenericString() + "] " +
                "with argument values:\n" + formattedArgs;
    }

}
